/*
	This file is part of Dope.

	Dope is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Dope is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Hash.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @file 
 * 
 * Contains function prototypes, typedefs and macro definitions you will need to work
 * with HASH structures.
 */
#ifndef HASH_H_
#define HASH_H_

#define HASH_MAJOR_VERSION 1
#define HASH_MINOR_VERSION 0
#define HASH_BUILD_VERSION 1

#include <string.h>
#include <stdlib.h>

#ifdef WIN32
#include <windows.h>
#else
#define BOOL	int
#define TRUE	1
#define FALSE	0
#endif

#define AND &&
#define OR ||
#define NOT !=
#define IS ==

/**
 * Pointer to a string
 */
typedef char *	PSTR;
/**
 * void pointer
 */
typedef void *	CAST;
/**
 * void
 */
typedef void	NONE;
/**
 * long
 */
typedef long	MASS;

/**
 * @brief 	Hash element / structure
 * @param	last	the next/last element in the list
 * @param	key		a pointer to the string containing the name
 * @param	value	a void pointer to the data linked
 * @param	root	boolean indication if this is the root of a HASH
 * 
 * It's all about this structure, all of it !!
 * You need to do your own typecasting when getting data in and out of a Hash, additionally
 * be careful of what memory you free, anything allocated is free'd when hashDestroy is invoked,
 * but NOT the data that each element links to, you have to take care of that yourself, as usual.
 */
typedef struct _hash
{
	/**
	 * Should probably be called next, only we go backwards, it's quite confusing !
	 */
	struct _hash * 	last ;
	/**
	 * I don't check for duplicates, so be sensible
	 */
	PSTR			key ;
	/**
	 * I don't copy memory ( by default ), so don't go free'ing things I'm using
	 */
	CAST			value ;
	/**
	 * Only ever true when hashCreate is called
	 */
	BOOL			root ;
	/**
	 * Element constructor, called when hashComplex/hashCopy is invoked
	 */
	BOOL			(*__constructor) ( struct _hash ** element);
	/**
	 * Element destructor, called when hashDestroy is invoked
	 */
	NONE			(*__destructor) ( struct _hash ** element);
	
} *					HASH;

/**
 * @brief 	Assign the base memory for a new hash
 * @return 	HASH
 * 
 * Will return a properly allocated HASH structure to use as the root of your Hash, this
 * memory is free'd when hashDestroy is invoked
 * If this function returns NULL you are out of memory and should probably exit
 */
HASH hashCreate( );

/**
 * @brief 	Assign a new element in the hash named key pointing at value
 * @return	BOOL
 * @param	hash 		pointer to the HASH being edited
 * @param	key			name of the new hash element
 * @param	value 		void pointer to the data you'd like to link
 * 
 * This adds a new element to your hash ( which should have been returned by hashCreate ).
 * Again, you must cast your data properly, and be sensible when naming new elements just like
 * creating normal variables.
 * If the key you're trying to add already exists hashEdit is called instead
 */
BOOL hashAdd( HASH * hash, PSTR key, CAST value );

/**
 * @brief 	Assign a new complex element in the hash named key pointing at value.
 * @return	BOOL
 * @param	hash 		pointer to the HASH being edited
 * @param	key			name of the new hash element
 * @param	value 		void pointer to the data you'd like to link
 * @param 	construct	pointer to a function that constructs the element
 * @param	destruct	pointer to a function to destruct the element
 * 
 * This adds a new element to your hash ( which should have been returned by hashCreate ).
 * Again, you must cast your data properly, and be sensible when naming new elements just like
 * creating normal variables.
 * Additionally, construct will be called on the newly created element and when hashDestroy
 * is invoked, destruct will be called on the element.
 * Destructors should NOT free the element itself, only the value of it, and possibly the key.
 * If a constructor fails FALSE is returned
 * If the key you're trying to add exists hashEdit is called instead, but constructors are not
 * set, it's fair to assume that if the element has the same key in the same has and you're trying
 * to apply a constructor that is was set when hashAdd was called.
 */
BOOL hashComplex( HASH * hash, PSTR key, CAST value, BOOL (*construct)( HASH *element ), NONE (*destruct)( HASH * element ) );

/**
 * @brief 	Update hash element key to point at value
 * @return	BOOL
 * @param	hash 		pointer to the HASH being edited
 * @param	key		name of the hash element to find
 * @param	value 		void pointer to the new data to be linked
 * 
 * This will update an an existing HASH in with the value pointed to by value. hash should have
 * been returned when createHash was invoked. If the hash cannot be found by key FALSE will be
 * returned.
 * If the HASH element found by hashEdit has a destructor, it will be called, so will the constructor
 * at the appropriate times to avoid memory leaks
 */
BOOL hashEdit( HASH * hash, PSTR key, CAST value );

/**
 * @brief 	Remove key from hash
 * @return	BOOL
 * @param	hash 		pointer to the HASH being edited
 * @param	key		name of the hash element to remove
 * 
 * This will remove one element by key from hash, and return a boolean indication of success
 */
BOOL hashRemove( HASH * hash, PSTR key );

/**
 * @brief 	Find element key in hash and return it's value
 * @return	CAST
 * @param	hash 		pointer to the HASH being searched
 * @param	key		name of the hash element to find
 * 
 * This returns the data in hash named by key, if the requested HASH cannot be found NULL will
 * be returned.
 */
CAST hashFind( HASH * hash, PSTR key );

/**
 * @brief 	Count total number of elements in hash
 * @return	MASS
 * @param	hash 		pointer to the HASH being counted
 * 
 * This will loop over all elements in a hash and count them, it does NOT count the size of the
 * data, should be called hashLength perhaps, too late now though !!
 */
MASS hashSize( HASH * hash );

/**
 * @brief 	Apply callback to every element in hash
 * @return	BOOL
 * @param	hash		
 * @param	callback 	pointer to a function that takes two argument as prototype suggests
 * 
 * This will loop over every element in hash and call the callback parameter passed.
 * The first argument to the callback will be the hash passed to hashApply
 * The second argument to the callback will be the current element in hash
 * The callback should return true if everything is well, returning false from the callback will
 * cause hashApply to also return false.
 */
BOOL hashApply( HASH * hash, BOOL (*callback)( HASH *, HASH ) );

/**
 * @brief	Clone HASH @ source to HASH @ target
 * @return	HASH
 * @param	hash		An already populated HASH
 * 
 * Return a clone of hash in newly allocated memory, everything is copied, constructors
 * are also called
 * NULL is returned on failure
 */
HASH hashClone( HASH * hash );

/**
 * @brief	Merges contents of merge with hash
 * @return	HASH
 * @param	hash		The hash you would like to merge data to
 * @param	merge		The hash you would like to merge data from
 * 
 * Copies elements from merge into hash, constructors/destructors are set and called
 * while copying
 */
BOOL hashMerge( HASH * hash, HASH * merge );

/**
 * @brief 	Release memory associated with hash
 * @return 	NONE
 * @param	hash		pointer to the HASH you want to release
 * 
 * You should always call this if you don't want memory leeks, after execution hash will be useless.
 */
NONE hashDestroy( HASH * hash );

/**
 * @brief 	Start looping over root using loop as element
 * @param 	hash 		root of hash to loop over
 * @param 	loop 		temporary HASH struct to use inside loop
 * 
 * HASH_L_* macros are to aid looping over elements in a HASH with very little typing, or thought ...
 * This takes a pointer to a HASH as the first argument and a HASH as the second, the second
 * is used temporarily while looping as a pointer, you do not need to allocate or free it.
 * HASH_L_START sets up a loop.
 * NOTICE: that are no commas at the end of HASH_L_* macros.
 * <pre>
 * ...
 * HASH root = hashCreate( );
 * 
 * hashAdd( &root, "key", "value" );
 * ...
 * HASH loop = NULL;
 * HASH_L_START( &root, loop )
 * {
 * 		//do whatever with loop->key and loop->value
 * }
 * HASH_L_END( &root, loop )
 * </pre>
 */
#define HASH_L_START(hash, loop) 	if( hash NOT NULL AND (*hash)->root NOT TRUE ) { loop = *hash; do
/**
 * @brief 	Continue looping over root using loop as element
 * @param 	hash 		root of hash to loop over
 * @param 	loop 		temporary HASH struct to use inside loop
 * 
 * HASH_L_* macros are to aid looping over elements in a HASH with very little typing, or thought ...
 * This takes a pointer to a HASH as the first argument and a HASH as the second, the second
 * is used temporarily while looping as a pointer, you do not need to allocate or free it.
 * HASH_L_END finishes off a loop.
 * NOTICE: that are no commas at the end of HASH_L_* macros.
 */
#define HASH_L_END(hash, loop)		while( ( loop = loop->last ) NOT NULL AND loop->root IS FALSE ); }

#endif /*HASH_H_*/
